package models

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"sync"

	"github.com/fatih/set"
	"github.com/gorilla/websocket"
	"gorm.io/gorm"
)

// 消息结构体
type Message struct {
	gorm.Model
	SenderId   int64  `json:"senderId"`   // 发送者
	ReceiverId int64  `json:"receiverId"` // 接收者
	ChatType   int8   `json:"chatType"`   // 聊天类型 1.私聊 2.群聊 3.广播
	MsgType    int8   `json:"msgType"`    // 消息类型 1.文字 2.图片 3.音频/视频
	Content    string `json:"content"`    // 消息内容
	RoomId     int    `json:"roomId"`     // 房间ID
}

type Node struct {
	Conn      *websocket.Conn
	DataQueue chan []byte
	GroupSets set.Interface
}

// 映射关系
var clientMap map[int64]*Node = make(map[int64]*Node, 0)

// 读写锁
var rwLocker sync.RWMutex

// 发送和接收消息
func Chat(writer http.ResponseWriter, request *http.Request) {
	// 获取参数并做token校验
	query := request.URL.Query()
	Id := query.Get("userId")
	fmt.Println(Id)
	if Id == "" {
		return
	}
	userId, _ := strconv.ParseInt(Id, 10, 64)
	fmt.Println(userId, query)
	// 是否允许链接
	isValida := true
	// 建立链接
	conn, err := (&websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return isValida
		},
	}).Upgrade(writer, request, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println("websocket connect success")
	// 获取 conn 实例化 Node 结构体
	node := &Node{
		Conn:      conn,
		DataQueue: make(chan []byte, 50),
		GroupSets: set.New(set.ThreadSafe),
	}
	// 加锁
	rwLocker.Lock()
	clientMap[userId] = node
	// 释放锁
	rwLocker.Unlock()
	// 完成发送逻辑
	go sendProc(node)
	// 完成接收逻辑
	go receiverProc(node)
	// 欢迎语
	sendMsg(userId, []byte("欢迎进入聊天室"))
}

// 发送消息的逻辑
func sendProc(n *Node) {
	for {
		select {
		case data := <-n.DataQueue:
			fmt.Println("send >>>", string(data))
			err := n.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

// 接收消息的逻辑
func receiverProc(n *Node) {
	for {
		_, data, err := n.Conn.ReadMessage()
		if err != nil {
			panic(err)
		}
		fmt.Println("receiver <<<", string(data))
		broadMsg(data)
	}
}

var udpsendChan chan []byte = make(chan []byte, 1024)

func broadMsg(data []byte) {
	udpsendChan <- data
}

func init() {
	go udpSendProc()
	go udpReceiverProc()
}

// 完成udp数据发送协程
func udpSendProc() {
	con, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4zero,
		Port: 3000,
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	defer con.Close()
	for {
		select {
		case data := <-udpsendChan:
			_, err := con.Write(data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

// 完成udp数据接收协程
func udpReceiverProc() {
	con, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4zero,
		Port: 3000,
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	defer con.Close()
	for {
		var buf [512]byte
		n, err := con.Read(buf[0:])
		if err != nil {
			fmt.Println(err)
			return
		}
		dispatch(buf[0:n])
	}
}

func dispatch(data []byte) {
	msg := Message{}
	err := json.Unmarshal(data, &msg)
	if err != nil {
		fmt.Println(err)
		return
	}
	switch msg.ChatType {
	case 1: // 私聊
		sendMsg(msg.ReceiverId, data)
	}
}

// 发送
func sendMsg(userId int64, msg []byte) {
	rwLocker.RLock()
	node, ok := clientMap[userId]
	rwLocker.RUnlock()
	if ok {
		node.DataQueue <- msg
	}
}
